package dryven.request.controller.paramtransform.baseimpl;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;

import dryven.config.DatabaseConfigurationSource;
import dryven.model.di.LocalThreadStorage;
import dryven.persistence.PersistenceDependencyInjectingBinder;
import dryven.reflection.DeclaringMethodIterator;
import dryven.request.controller.paramtransform.ActionParameterBindCandidate;
import dryven.request.controller.paramtransform.ActionParameterTransformFeedback;
import dryven.request.controller.paramtransform.ActionParameterTransformationException;
import dryven.request.http.Request;
import dryven.request.http.Response;

public class EntityInjectingBinder implements ActionParameterBindCandidate {
	
	private PersistenceDependencyInjectingBinder _emBinder;
	
	public EntityInjectingBinder(DatabaseConfigurationSource dbSource) {
		this(new PersistenceDependencyInjectingBinder(dbSource));
	}
	
	public EntityInjectingBinder(PersistenceDependencyInjectingBinder emBinder) {
		_emBinder = emBinder;
	}

	@Override
	public void apply(Class<?> type, Annotation[] annotations,
			Object currentValue, Request request, Response response, ActionParameterTransformFeedback feedback) {
		if(!(currentValue instanceof String || currentValue instanceof Integer)) {
			return;
		}
		int id = Integer.parseInt(currentValue.toString());
		Object model = loadEntity(type, id, request);
		if(model!=null) {
			feedback.setParameter(model);
		}
	}

	public <T> T loadEntity(Class<T> type, int id, LocalThreadStorage storage) {
		EntityManager em = (EntityManager) _emBinder.load(storage, type, null);
		T model = em.find(type, id);
		return model;
	}

	@Override
	public boolean canApply(Class<?> type, Annotation[] annotations) {
		return type.getAnnotation(Entity.class)!=null;
	}

	@Override
	public boolean isValueGenerator() {
		return false;
	}

	@Override
	public String serializeParameter(Object o) {
		Object pk = null;
		for (Method m : new DeclaringMethodIterator(o.getClass())) {
			if(m.getAnnotation(Id.class)!=null) {
				try {
					pk = m.invoke(o, new Object[]{});
				} catch (Exception e) {
					throw new RuntimeException("Could not get PK");
				}
			}
		}
		if(pk instanceof Integer) {
			Integer pkInt = (Integer)pk;
			if(pkInt.intValue()==0) {
				return null;
			}
		}
		return pk.toString();
	}

}
